#include <fstream>
#include <sstream>
#include <kuai/misc/typedef.hpp>
#include <kuai/misc/strtool.hpp>
#include <kuai/misc/funcman.hpp>


#ifndef _KUAI_MISC_IOTOOL_H_
#define _KUAI_MISC_IOTOOL_H_

namespace kuai
{ 
	template<typename DataType>
		class Reader
	{
	public:
		typedef DataType Value;
		typedef DataType& Reference;
	public:
		virtual ~Reader()
		{ }
		
		/**
		*	This function read a data from a stream
		*/
		virtual bool update(std::istream& stream, Reference data) const = 0;

		virtual bool read(std::istream& stream, Reference data) const {
			DataType result;
			try {
				if (update(stream, result)) {
					swap(data, result);
					return true;
				}
				else { 
					return false;
				}
			}
			catch (std::exception&) {
				return false;
			}
		}

		/**
		*	This function read a data from a file
		*/
		virtual bool read(const FileName& filename, Reference result) const
		{ 
			std::ifstream stream;
			tryOpen(stream, filename);
			return read(stream, result);
		}

		virtual bool parse(const Char szData[], Reference data) const {
			std::istringstream stream(szData);
			return read(stream, data);
		}
	};

	template<typename DataType>
		class Writer
	{
	public:
		typedef DataType Value;
		typedef const DataType& Reference;
	public:
		virtual ~Writer()
		{ }
		
		/**
		*	This function read a data from a stream
		*/
		virtual bool write(std::ostream& stream, Reference data) const = 0;

		/**
		*	This function read a data from a file
		*/
		virtual bool write(const FileName& filename, Reference result) const
		{ 
			std::ofstream stream;
			tryOpen(stream, filename);
			return write(stream, result);
		}

		String format(Reference data) const {
			std::ostringstream stream;
			if (write(stream, data)) {
				return stream.str();
			}
			else {
				return String();
			}
		} 
	};

	template<typename DataType>
		class ReaderManager
			: public FunctionManager<String, Reader<DataType> >
	{ 
	public:
		typedef Reader<DataType> ReaderType;
		typedef const boost::shared_ptr<ReaderType>  ReaderPointer;
		typedef typename ReaderType::Value Value;
		typedef typename ReaderType::Reference Reference;

	public:
		virtual bool read(std::istream& stream, const String& type, Reference data) const
		{
			if (const ReaderType* reader = getReader(type))
			{ 
				return reader->read(stream, data);
			}
			else
			{
				return false;
			}
		}

		virtual bool read(const FileName& filename, Reference result) const
		{ 
			String type = filename.extname();
			return read(filename, type, result);
		}
		
		virtual bool read(const FileName& filename, const String& type, Reference result) const
		{ 
			if (const ReaderType* reader = getReader(type))
			{ 
				return reader->read(filename, result);
			}
			else
			{
				return false;
			}
		}

		virtual bool parse(const String& data, const String& type, Reference result) const
		{ 
			if (const ReaderType* reader = getReader(type))
			{ 
				std::istringstream iss(data);
				return reader->read(iss, result);
			}
			else
			{
				return false;
			}
		}

	protected:
		virtual const ReaderType* getReader(const String& key) const
		{
			String type = boost::algorithm::to_lower_copy(key);
			if (const ReaderType* result = get(type))
			{ 
				return result;
			}
			else
			{
				return get("");
			}
		}
	};

	template<typename DataType>
		class WriterManager
			: public FunctionManager<String, Writer<DataType> >
	{ 
	public:
		typedef Writer<DataType> WriterType;
		typedef const WriterType* WriterPointer;
		typedef typename WriterType::Value Value;
		typedef typename WriterType::Reference Reference;

	public:
		virtual WriterPointer getWriter(const String& key) const
		{
			String type = boost::algorithm::to_lower_copy(key);
			if (WriterPointer result = get(type))
			{ 
				return result;
			}
			else
			{
				return get("");
			}
		}

		virtual bool write(std::ostream& stream, const String& type, Reference data) const 
		{ 
			if (WriterPointer writer = getWriter(type))
			{ 
				return writer->write(stream, data);
			}
			else
			{
				return false;
			}
		}

		virtual bool write(const FileName& filename, Reference result) const
		{ 
			String type = filename.extname();
			return write(filename, type, result);
		}
		virtual bool write(const FileName& filename, const String& type, Reference result) const
		{ 
			if (WriterPointer writer = getWriter(type))
			{ 
				return writer->write(filename, result);
			}
			else
			{
				return false;
			}
		}
	};



}

#endif
